﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Threading;

namespace CarrouselPanel
{
    public class CarrouselPanel : Panel
    {
        #region circleWidthProperty
        public static readonly DependencyProperty circleWidthProperty = DependencyProperty.Register("circleWidth", typeof(double), typeof(CarrouselPanel), new PropertyMetadata(200.00, OncircleWidthChanged));
        public double circleWidth
        {
            get
            {
                return (double)base.GetValue(circleWidthProperty);
            }
            set
            {
                base.SetValue(circleWidthProperty, value);
            }
        }
        private static void OncircleWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as CarrouselPanel).count = 0;
        }
        #endregion 

        #region circleHeightProperty
        public static readonly DependencyProperty circleHeightProperty = DependencyProperty.Register("circleHeight", typeof(double), typeof(CarrouselPanel), new PropertyMetadata(10.00, OncircleHeightChanged));
        public double circleHeight
        {
            get
            {
                return (double)base.GetValue(circleHeightProperty);
            }
            set
            {
                base.SetValue(circleHeightProperty, value);
            }
        }
        private static void OncircleHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as CarrouselPanel).count = 0;
        }
        #endregion 

        #region distanceProperty
        public static readonly DependencyProperty distanceProperty = DependencyProperty.Register("distance", typeof(Distance), typeof(CarrouselPanel), new PropertyMetadata(Distance.Left));
        public Distance distance
        {
            get
            {
                return (Distance)base.GetValue(distanceProperty);
            }
            set
            {
                base.SetValue(circleHeightProperty, value);
            }
        }
        #endregion

        #region autoPlayProperty
        public static readonly DependencyProperty autoPlayProperty = DependencyProperty.Register("autoPlay", typeof(bool), typeof(CarrouselPanel), new PropertyMetadata(true));
        public bool autoPlay
        {
            get
            {
                return (bool)base.GetValue(autoPlayProperty);
            }
            set
            {
                base.SetValue(autoPlayProperty, value);
            }
        }
        #endregion


        #region currElement (DependencyProperty)

        /// <summary>
        /// A description of the property.
        /// </summary>
        public FrameworkElement currElement
        {
            get { return (FrameworkElement)GetValue(currElementProperty); }
            set { SetValue(currElementProperty, value); }
        }
        public static readonly DependencyProperty currElementProperty =
            DependencyProperty.Register("currElement", typeof(FrameworkElement), typeof(CarrouselPanel),
              new PropertyMetadata(null));

        #endregion



        private Dictionary<FrameworkElement, ElementStatus> _InternalChildren = new Dictionary<FrameworkElement, ElementStatus>();
        private int _NextHighestDepth;
        private double dis;
        private int count;
        private Point Center {
            get { return new Point(base.ActualWidth / 2, base.ActualHeight / 2); }
        }
        private double  speed = 0.1;
        private double viewPosition = 0;
        private bool IsMouseOver = false;
        private int timer = 0;
        private int frameTime = 150;
        private double maxScale = 1;
        private double minScale = 0.4;

        private double maxAlpha = 1;
        private double minAlpha = 0.8;

        private List<double> listScale;
        private List<double> listAlpha;

        private bool IsMove = false;

        public delegate void CarrouselPanelItemClickedHandler(CarrouselPanel sender, CarrouselPanel.CarrouselPanelItemClickedEventArgs e);
        public class CarrouselPanelItemClickedEventArgs : RoutedEventArgs
        {
            // Fields
            public readonly FrameworkElement ClickedItem;

            // Methods
            public CarrouselPanelItemClickedEventArgs(FrameworkElement clickedItem)
            {
                this.ClickedItem = clickedItem;
            }
        }

        // Events
        public event CarrouselPanelItemClickedHandler CarrouselPanelItemClicked;

        public CarrouselPanel()
            : base()
        {
            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
        }
        public void AddChild(FrameworkElement child)
        {
            TranslateTransform xTranslateTransform = new TranslateTransform();
            RotateTransform xRotateTransform = new RotateTransform();
            ScaleTransform xScaleTransform = new ScaleTransform();
            SkewTransform xSkewTransform = new SkewTransform();
            TranslateTransform RegisterTranslate = new TranslateTransform();
            child.RenderTransform = new TransformGroup { Children = new TransformCollection { xRotateTransform, xScaleTransform, xSkewTransform, xTranslateTransform, RegisterTranslate } };
            ElementStatus status = new ElementStatus
            {
                xRotateTransform = xRotateTransform,
                xScaleTransform = xScaleTransform,
                xSkewTransform = xSkewTransform,
                xTranslateTransform = xTranslateTransform,
                RegisterTranslate = RegisterTranslate
            };
            this._InternalChildren.Add(child, status);
            this._NextHighestDepth++;
            child.MouseEnter += new MouseEventHandler(child_MouseEnter);
            child.MouseLeave += new MouseEventHandler(child_MouseLeave);
            base.Children.Add(child);
            InitChildren();
        }

        void child_MouseLeave(object sender, MouseEventArgs e)
        {
            IsMouseOver = false;
            timer = 1;
        }

        void child_MouseEnter(object sender, MouseEventArgs e)
        {
            IsMouseOver = true;
        }

        private void InitChildren()
        {
            int i = 1;
            initScaleAndAlpha();
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status = this._InternalChildren[element];
                status.target = 6.283185E+000 * (i - 1) / this._InternalChildren.Count + (1.570796E+000 + viewPosition);
                status.s = status.target;
                status.i = i;
                status.scale = listScale[i - 1];
                status.alpha = listAlpha[i - 1];
                element.MouseLeftButtonUp += new MouseButtonEventHandler(element_MouseLeftButtonUp);
                i++;
                if (status.i == 1)
                {
                    currElement = element;
                }
            }
        }

        void element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            ElementStatus status = this._InternalChildren[element];
            if (status.i == 1)
            {
                if (!IsMove)
                {
                    if (this.CarrouselPanelItemClicked != null)
                    {
                        this.CarrouselPanelItemClicked(this, new CarrouselPanelItemClickedEventArgs((FrameworkElement)sender));
                    }
                }
            }
            else if (status.xTranslateTransform.X > Center.X)
            {
                var jumpNum = this._InternalChildren.Count - status.i + 1;
                Move(jumpNum, Distance.Right);
            }
            else
            {
                var jumpNum = status.i - 1;
                Move(jumpNum, Distance.Left);
            }
        }

        private void initScaleAndAlpha()
        {
            listScale = new List<double>();
            listAlpha = new List<double>();
            double stepScale = (maxScale - minScale) / (this._InternalChildren.Count / 2);
            double stepAlpha = (maxAlpha - minAlpha) / (this._InternalChildren.Count / 2);
            int hnum = ((this._InternalChildren.Count / 2.0) > Convert.ToInt32(this._InternalChildren.Count / 2)) ? Convert.ToInt32(this._InternalChildren.Count / 2) + 1 : Convert.ToInt32(this._InternalChildren.Count / 2);
            for (int i = 0; i < this._InternalChildren.Count; i++)
            {
                if (i < hnum)
                {
                    listScale.Add((maxScale - stepScale * i));
                    listAlpha.Add((maxAlpha - stepAlpha * i));
                }
                else
                {
                    listScale.Add((minScale + stepScale * (i - hnum)));
                    listAlpha.Add((minAlpha + stepAlpha * (i - hnum)));
                }
            }
        }
        public void RemoveChild(FrameworkElement child) { }
        public void RemoveAllChildren()
        {
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                base.Children.Remove(element);
            }
            _InternalChildren = new Dictionary<FrameworkElement, ElementStatus>();
        }
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (count++ < 100)
            {
                int i = 1;
                foreach (FrameworkElement element in this._InternalChildren.Keys)
                {
                    ElementStatus status = this._InternalChildren[element];
                    status.RegisterTranslate.X = -element.ActualWidth / 2.0;
                    status.RegisterTranslate.Y = -element.ActualHeight / 2.0;
                    status.xTranslateTransform.X = circleWidth * Math.Cos(status.t) + Center.X;
                    status.xTranslateTransform.Y = circleHeight * Math.Sin(status.t) + Center.Y;
                    status.t = status.t + speed * (status.target - status.t);
                    Canvas.SetZIndex(element, Convert.ToInt32(status.xTranslateTransform.Y));
                    dis = 6.283185E+000 / this._InternalChildren.Count;
                    if (count > 1)
                    {
                        status.xScaleTransform.ScaleX = status.xScaleTransform.ScaleX + speed * (status.scale - status.xScaleTransform.ScaleX);
                        status.xScaleTransform.ScaleY = status.xScaleTransform.ScaleX;
                        element.Opacity = element.Opacity + speed * (status.alpha - element.Opacity);
                    }
                    i++;
                }
            }
            else {
                IsMove = false;
            }
            if (autoPlay)
            {
                if (!IsMouseOver)
                {
                    if ((timer++ % frameTime) == 0)
                    {
                        Move(1, distance);
                    }
                }
            }
        }

        public void Move(int r, Distance distance)
        {
            IsMove = true;
            int k = 1;
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status = this._InternalChildren[element];
                if (distance == Distance.Right)
                {
                    status.target = status.s + 6.283185E+000 / this._InternalChildren.Count + dis * (r - 1);
                    status.s = status.target;
                    for (var j = 1; j <= r; ++j)
                    {
                        if (status.i == this._InternalChildren.Count)
                        {
                            status.i = 1;
                            continue;
                        }
                        ++status.i;
                    }
                }
                else
                {
                    status.target = status.s - 6.283185E+000 / this._InternalChildren.Count - dis * (r - 1);
                    status.s = status.target;
                    for (var j = 1; j <= r; ++j)
                    {
                        if (status.i == 1)
                        {
                            status.i = this._InternalChildren.Count;
                            continue;
                        }
                        --status.i;
                    }
                }
                if (status.i == 1)
                {
                    currElement = element;
                }

                status.scale = listScale[status.i - 1];
                status.alpha = listAlpha[status.i - 1];

                k++;
            }

            count = 0;
        }

        #region 必备方法
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement element in base.Children)
            {
                Point location = new Point();
                element.Arrange(new Rect(location, element.DesiredSize));
            }
            return finalSize;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement element in base.Children)
            {
                element.Measure(availableSize);
            }
            base.Clip = new RectangleGeometry { Rect = new Rect(new Point(), availableSize) };
            return availableSize;
        }
        #endregion

        private class ElementStatus
        {
            internal TranslateTransform xTranslateTransform;
            internal RotateTransform xRotateTransform;
            internal ScaleTransform xScaleTransform;
            internal SkewTransform xSkewTransform;
            internal TranslateTransform RegisterTranslate;
            internal double t;
            internal double s;
            internal double target;
            internal double alpha;
            internal double scale;
            internal int i;
        }

    }
    public enum Distance { Left, Right }

}